/*
 * RTSAI - Real-Time Strategy engine for AI research
 * Copyright(C) 2006 DIMAp-UFRN, licensed under LGPL
 *
 * GameView implementation
 */

#include "Draw.h"            // OpenSteerDemo graphics
#include "Game.h"
#include "GameView.h"

// initialization of static atributes
OpenSteer::Clock            GameView :: clock;
OpenSteer::Camera           GameView :: camera;
const OpenSteer::AbstractVehicle* GameView :: selectedVehicle = NULL;

int  GameView :: mouseX = 0;
int  GameView :: mouseY = 0;
bool GameView :: mouseInWindow = false;

ViewCallback GameView::visualization_update = NULL;

const Vec3 player_colors[] = {
    Vec3(0.7,0.7,0.5),
    Vec3(0.5,0.7,0.7),
    Vec3(0.7,0.7,0.5),
    Vec3(0.5,0.7,0.5),
    Vec3(0.5,0.5,0.7),
    Vec3(0.7,0.5,0.5),
};


GameView::GameView()
{
    //ctor
}

GameView::~GameView()
{
    //dtor
}

/*! Game view initialization
 */
void GameView :: init (int argc, char **argv, ViewCallback view_update)
{
    LOG_DEBUG << "Initializating game view" << std::endl;

    // initialize camera
    camera.reset();
    camera.setPosition ( 10, camera2dElevation, 10 );
    camera.fixedPosition.set ( 40, 40, 40 );

    // initialize graphic window
    OpenSteer::initializeGraphics (argc, argv);

    // initialize visualization callback
    visualization_update = view_update;

    clock.setPausedState ( true );
}


/*! Game view loop
 */
void GameView :: start_loop()
{
    LOG_DEBUG << "Running game" << std::endl;

    // run the main event processing loop
    OpenSteer::runGraphics ();
}


/*! Main update function: step simulation forward and redraw scene
 */
void GameView :: update()
{
    selectedVehicle = NULL; // reset the selected vehicle on each iteration

    logical_update();       // update the game logics (rtsai)
    visualization_update(); // call the application visualization callback

    // update camera according to the selected vehicle
    Game &game = Game :: getInstance();
    updateCamera (game.current_time, game.elapsed_time, *selectedVehicle);
    // gridUtility (Vector3D::zero);
}


/*! Update clocks and call update.
 *  Step simulation foward
 */
void GameView :: logical_update ()
{
    // update global simulation clock
    clock.update ();

    Game &game = Game :: getInstance();
    game.current_time = clock.getTotalSimulationTime ();
    game.elapsed_time = clock.getElapsedSimulationTime ();

    if ( !clock.getPausedState() ) {
        game.update();
    }
}


/*! Draw a vehicle
 *  @param unit The owner unit of the vehicle
 *  @param coloc Vehicle color defined as Vector3D (RGB)
 */
void GameView :: drawVehicle (const Unit& unit, const Vector3D& color)
{
    if (!selectedVehicle) {
        selectedVehicle = &unit;
    }
    drawBasic3dSphericalVehicle (unit, color);
}


/*! Draw the game object.
 *  For now, just drawing circles
 */
void GameView :: drawObject (const GameObject& obj, const Vector3D& color)
{
    // TODO test if ( typeid (*obj) == typeid (SphObject) )
    const SphObject *sph = dynamic_cast < const SphObject* > ( &obj );
    drawXZCircle (sph->radius, sph->center, color, 10);
}


// ----------------------------------------------------------------------------
/*! Set a certain initial camera state
 */
void GameView :: init3dCamera (AbstractVehicle& selected,
                                  float distance,
                                  float elevation)
{
    position3dCamera (selected, distance, elevation);
    camera.fixedDistDistance = distance;
    camera.fixedDistVOffset = elevation;
    camera.mode = Camera::cmFixedDistanceOffset;
}


/*! Set a certain initial camera state
 */
void GameView :: init2dCamera (AbstractVehicle& selected,
                                  float distance,
                                  float elevation)
{
    position2dCamera (selected, distance, elevation);
    camera.fixedDistDistance = distance;
    camera.fixedDistVOffset = elevation;
    camera.mode = Camera::cmFixedDistanceOffset;
}


/*! Set a certain initial camera position
 */
void GameView :: position3dCamera (AbstractVehicle& selected,
                                            float distance,
                                            float /*elevation*/)
{
    selectedVehicle = &selected;
    if (&selected)
    {
        const Vec3 behind = selected.forward() * -distance;
        camera.setPosition (selected.position() + behind);
        camera.target = selected.position();
    }
}


/*! Set a certain initial camera position
 */
void GameView :: position2dCamera (AbstractVehicle& selected,
                                            float distance,
                                            float elevation)
{
    // position the camera as if in 3d:
    position3dCamera (selected, distance, elevation);

    // then adjust for 3d:
    Vec3 position3d = camera.position();
    position3d.y += elevation;
    camera.setPosition (position3d);
}


/*! Update the camera
 */
void GameView :: updateCamera (const float currentTime,
                               const float elapsedTime,
                               const AbstractVehicle& selected)
{
    camera.vehicleToTrack = &selected;
    camera.update (currentTime, elapsedTime, clock.getPausedState ());
}


/*! Draw the grid (like a game map)
 */
void GameView :: gridUtility (const Vec3& gridTarget)
{
    // round off target to the nearest multiple of 2 (because the
    // checkboard grid with a pitch of 1 tiles with a period of 2)
    // then lower the grid a bit to put it under 2d annotation lines
    const Vec3 gridCenter ((round (gridTarget.x * 0.5f) * 2),
                           (round (gridTarget.y * 0.5f) * 2) - .05f,
                           (round (gridTarget.z * 0.5f) * 2));

    // colors for checkboard
    const Vec3 gray1 = grayColor (0.27f);
    const Vec3 gray2 = grayColor (0.30f);

    // draw 50x50 checkerboard grid with 50 squares along each side
    drawXZCheckerboardGrid (50, 50, gridCenter, gray1, gray2);

    // alternate style
    // drawXZLineGrid (50, 50, gridCenter, black);
}
